package model.datareader;

import java.util.ArrayList;
import java.util.List;
import model.exception.FileReadException;
import datacontainer.ImmuTable;


/**
 * Abstract class for the reader hierarchy. This hierarchy will contain all the
 * readers
 * required for reading different data formats.
 * 
 * Each reader will have methods to take in a file path to the file and read in
 * the data.
 * The reader will create a Result class to hold the data it pulls in. It will
 * pass this
 * Result object to Main.
 * 
 * @author Michael Deng
 * @author Jei min Yoo
 * 
 */
public abstract class AbstractReader {

    private static final int ONE = 1;
    private static final String[] COLUMNS_TO_DELETE = {"AP Pre", "AP High", "AP Final", "Notes"};
    private static final String[] REQUIRED_HEADERS = {"Season", "School", "Conf", "G", "W", "L",
                                                      "W-L%", "SRS", "SOS"};

    private List<List<String>> myData;
    private List<String> myHeader;
    private List<String> myType;

    /**
     * constructor initializes data structures
     */
    public AbstractReader () {
        createDataStructure();
    }

    protected List<List<String>> getData () {
        return myData;
    }

    private void createDataStructure() {
        myData = new ArrayList<List<String>>();
        myHeader = new ArrayList<String>();
        myType = new ArrayList<String>();
    }

    /**
     * reader reads file, establishes the data structures
     * reader also checks and trims data.
     * 
     * @param filePath file path to pull file from
     */
    public void readFile (String filePath) {
        extractData();
        myHeader = myData.remove(0);
        formColumnStructure();
        determineType();
        checkData();
        trimData();
    }

    private void trimData () {
        trimRows();
        trimColumns();
        trimSchoolNames();
    }

    private void trimSchoolNames () {
        int schoolIndex = myHeader.indexOf("School");
        for (int i = 0; i < myData.get(schoolIndex).size(); i++) {
            String cell = myData.get(schoolIndex).get(i);
            if (cell.contains("*")) {
                int starIndex = cell.indexOf("*");
                cell = cell.substring(0, starIndex);
                myData.get(schoolIndex).set(i, cell);
            }
        }
    }

    private void trimRows () {
        int colNum = myHeader.indexOf("Season");
        int rowNum = myData.get(colNum).indexOf("Career");
        for (int i = 0; i < myData.size(); i++) {
            List<String> column = myData.get(i);
            myData.set(i, column.subList(0, rowNum));

        }
    }

    private void trimColumns () {
        for (String headerToDelete : COLUMNS_TO_DELETE) {
            myData.remove(myHeader.indexOf(headerToDelete));
            myHeader.remove(myHeader.indexOf(headerToDelete));
        }
    }

    private void checkData () {
        checkType();
        checkHeader();
    }

    private void checkType () {
        for (int i = 0; i < myHeader.size(); i++) {
            for (String cell : myData.get(i)) {
                if (!"".equals(cell)) {
                    if (!getType(cell).equals(myType.get(i))) { 
                        throw new FileReadException("A column type is incorrect!");
                    }
                }
            }
        }
    }

    private void checkHeader () {
        for (String head : REQUIRED_HEADERS) {
            if (!myHeader.contains(head)) { 
                throw new FileReadException("Data is missing a required header: " + head + "!");
            }
        }
    }

    private String getType (String cell) {
        try {
            Double.parseDouble(cell);
            return "Double";
        }
        catch (NumberFormatException e) {
            return "String";
        }
    }

    protected abstract void extractData ();

    private void determineType () {
        for (int i = 0; i < myHeader.size(); i++) {
            for (String cell : myData.get(i)) {
                if (!"".equals(cell)) {
                    myType.add(getType(cell));
                    break;
                }
            }
            if (myType.size() != i + ONE) {
                myType.add("String");
            }
        }
    }

    private void formColumnStructure () {
        List<List<String>> tempData = new ArrayList<List<String>>();
        for (int col = 0; col < myHeader.size(); col++) {
            tempData.add(new ArrayList<String>());
            for (int row = 0; row < myData.size(); row++) {
                if (col >= myData.get(row).size()) {
                    tempData.get(col).add("");
                }
                else {
                    tempData.get(col).add(myData.get(row).get(col));
                }
            }
        }
        myData = tempData;
    }

    /**
     * returns the immutable raw data from the file.
     * 
     * @return ImmuTable object containing raw data
     */
    public ImmuTable returnResult () {
        ImmuTable table = new ImmuTable(myHeader, myType, myData);
        table.makeUnmodifiable();
        // creates new data structures to replace those just given to ImmuTable
        createDataStructure(); 
        return table;
    }
}
